Skip to content

gh-156207: Fix curses Textbox.gather() for double-width characters - #156208

Merged
serhiy-storchaka merged 3 commits into
python:mainfrom
fedonman:fix-textpad-gather-wide
Aug 25, 2026
Merged

gh-156207: Fix curses Textbox.gather() for double-width characters#156208
serhiy-storchaka merged 3 commits into
python:mainfrom
fedonman:fix-textpad-gather-wide

Conversation

@fedonman

Copy link
Copy Markdown
Contributor

gather() now reads each line with in_wchstr(), whose count is a cell count and which skips a double-width character's continuation cell, instead of reading one cell at a time and reporting that second cell as another copy of the character.

This fixes only the gather() symptom of gh-156207. The insert-mode corruption and the curses.error at the last column are untouched: both need _insert_printable_char() to know a character's display width, which is a larger change.

$ ./python -m test test_curses -u curses
Using random seed: 2577236069
0:00:00 load avg: 3.88 mem: 22.8 MiB Run 1 test sequentially in a single process
0:00:00 load avg: 3.88 mem: 22.8 MiB [1/1] test_curses
== Tests result: SUCCESS ==
Total tests: run=181
Result: SUCCESS

The duplication only exists on the main branch, where gather() started reading cells with in_wch(), so there is no NEWS entry.

gather() read the window one cell at a time, so the second cell of a
double-width character was reported as another copy of it.  Read each
line with in_wchstr() instead: its count is a cell count, and it skips
the continuation cell.

@serhiy-storchaka serhiy-storchaka left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This all is right, but using the new API allows us to go further. We no longer need move() + _end_of_line(y) to get the count here. We can simply read to the end of line and then strip redundant spaces (except one). Probably we can even use in_wstr().

in_wstr() reads a line to its end, so gather() no longer needs move() +
_end_of_line() to work out how much of it to ask for: strip the trailing
blanks instead, keeping the one the cursor rests on.
@fedonman

Copy link
Copy Markdown
Contributor Author

Thanks, done.

Comment thread Lib/test/test_curses.py Outdated
# reads a whole line at a time so that the second cell, which holds
# the same character, is not reported as another one.
text = '你好'
if self._encodable(text):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Skip the test if it is not encodable.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, done.

Comment thread Lib/test/test_curses.py Outdated
for ch in text:
box.do_command(ch)
self.assertEqual(box.gather(), text + ' ')
box, win = self._make_textbox(1, 12, stripspaces=0)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

stripspaces is boolean.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, done.

@serhiy-storchaka serhiy-storchaka left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. 👍

Thank you for your contribution @fedonman.

@serhiy-storchaka
serhiy-storchaka enabled auto-merge (squash) August 25, 2026 08:15
@serhiy-storchaka
serhiy-storchaka merged commit fe8ace3 into python:main Aug 25, 2026
54 checks passed
@serhiy-storchaka

Copy link
Copy Markdown
Member

@fedonman, please say your AI to restore the bedevere links.

clin1234 pushed a commit to clin1234/cpython that referenced this pull request Aug 27, 2026
…ers (pythonGH-156208)

gather() read the window cell by cell, so a double-width character, which
occupies two cells, was returned twice.  Read the whole line with in_wstr(),
which returns every character once.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants